/*
 * Debug.cpp
 *
 *  Created on: 2010-9-10
 *      Author: liuyi
 */

#include "Debug.h"

#include <cstdarg>
#include <iostream>
#include <sstream>
#include <cstring>
#include <ctime>

#include <cv.h>
#include <highgui.h>

using namespace std;

const IplImage *Debug::s_originImage = NULL;

int Debug::s_trackbarPos = 20;

bool Debug::s_blindWindowOpen = false;

void Debug::showImage(const char *winName, const IplImage *image, int wait) {
	cvNamedWindow(winName);

	const int paramLen = strlen(winName) + 1;
	char param[paramLen];
	strcpy(param, winName);
	cvSetMouseCallback(winName, mouseCallback, param);

	cvCreateTrackbar("trackbar", winName, &s_trackbarPos, s_TRACKBAR_COUNT, trackbarCallback);

	s_originImage = image;

	cvShowImage(winName, s_originImage);

	char key;
	do {
		key = cvWaitKey(wait);

		if (key == 's') {
			stringstream ss;
			ss << time(NULL) % 1000000;
			ss << ".bmp";

			cvSaveImage(ss.str().c_str(), image);
		}
	} while (key == 's');

	cvDestroyWindow(winName);
}

/**
 * This is a function illustrating how to show more than one image in a single window using Intel OpenCV
 *
 * @param char *title: Title of the window to be showed
 * @param int nArgs:   Number of images to be showed
 * @param ...:         IplImage*, which contains the images
 *
 * The method used is to set the ROIs of a Single Big image and then resizing
 * and copying the input images on to the Single Big Image. This function does not
 * stretch the image, and it resizes the image without modifying the width/height ratio..
 *
 * This function can be called like this:
 * showManyImages("Images", 2, img1, img2);
 * or
 * showManyImages("Images", 5, img2, img2, img3, img4, img5);
 *
 * This function can show upto 12 images in a single window. It does not check whether
 * the arguments are of type IplImage* or not. The maximum window size is 700 by 660 pixels.
 * Does not show anything if the number of arguments is less than one or greater than 12.
 *
 * Take care of the number of arguments you pass, and the type of arguments, which should be
 * of type IplImage* ONLY. If you pass a pointer that is not IplImage*, Error will occur.
 */

void Debug::showManyImage(char *title, int nArgs, ...) {

	// img - Used for getting the arguments
	IplImage *img;

	// DispImage - the image in which input images are to be copied
	IplImage *DispImage;

	int size;
	int i;
	int m, n;
	int x, y;

	// w - Maximum number of images in a row
	// h - Maximum number of images in a column
	int w, h;

	// scale - How much we have to resize the image
	float scale;
	int max;

	// If the number of arguments is lesser than 0 or greater than 12
	// return without showing
	if (nArgs <= 0) {
		printf("Number of arguments too small....\n");
		return;
	} else if (nArgs > 12) {
		printf("Number of arguments too large....\n");
		return;
	} // Determine the size of the image,
	// and the number of rows/cols
	// from number of arguments
	else if (nArgs == 1) {
		w = h = 1;
		size = 300;
	} else if (nArgs == 2) {
		w = 2;
		h = 1;
		size = 300;
	} else if (nArgs == 3 || nArgs == 4) {
		w = 2;
		h = 2;
		size = 300;
	} else if (nArgs == 5 || nArgs == 6) {
		w = 3;
		h = 2;
		size = 200;
	} else if (nArgs == 7 || nArgs == 8) {
		w = 4;
		h = 2;
		size = 200;
	} else {
		w = 4;
		h = 3;
		size = 150;
	}

	// Create a new 3 channel image
	DispImage = cvCreateImage(cvSize(100 + size * w, 60 + size * h), 8, s_RGB_CHANNAL);

	// Used to get the arguments passed
	va_list args;
	va_start(args, nArgs);

	// Loop for nArgs number of arguments
	for (i = 0, m = 20, n = 20; i < nArgs; i++, m += (20 + size)) {

		// Get the Pointer to the IplImage
		img = va_arg(args, IplImage*);

		// Check whether it is NULL or not
		// If it is NULL, release the image, and return
		if (img == 0) {
			printf("Invalid arguments");
			cvReleaseImage(&DispImage);
			return;
		}

		// Find the width and height of the image
		x = img->width;
		y = img->height;

		// Find whether height or width is greater in order to resize the image
		max = (x > y) ? x : y;

		// Find the scaling factor to resize the image
		scale = (float) ((float) max / size);

		// Used to Align the images
		if (i % w == 0 && m != 20) {
			m = 20;
			n += 20 + size;
		}

		// Set the image ROI to show the current image
		cvSetImageROI(DispImage, cvRect(m, n, (int) (x / scale), (int) (y
				/ scale)));

		// Resize the input image and copy the it to the Single Big Image
		cvResize(img, DispImage);

		// Reset the ROI in order to show the next image
		cvResetImageROI(DispImage);
	}

	// Create a new window, and show the Single Big Image
	cvNamedWindow(title, 1);
	cvShowImage(title, DispImage);

	cvWaitKey();
	cvDestroyWindow(title);

	// End the number of arguments
	va_end(args);

	// Release the Image Memory
	cvReleaseImage(&DispImage);
}

void Debug::setBlindWindow(const char *winName, int x, int y) {
	int width = s_originImage->width;
	int height = s_originImage->height;

	int size = s_trackbarPos;
	if(size > width) {
		size = width;
	}
	if(size > height) {
		size = height;
	}
	if(x + size > width) {
		x = width - size;
	}
	if(y + size > height) {
		y = height - size;
	}

	IplImage *disp = cvCloneImage(s_originImage);
	memset(disp->imageData, 0, disp->imageSize);

	int step = disp->widthStep;
	for (int i = 0; i < size; ++i) {
		memcpy(disp->imageData + step * (y + i) + s_RGB_CHANNAL * x,
				s_originImage->imageData + step * (y + i) + s_RGB_CHANNAL * x,
				s_RGB_CHANNAL * size);
	}

	cvShowImage(winName, disp);

	cvReleaseImage(&disp);
}

void Debug::mouseCallback(int event, int x, int y, int flags, void *param) {
	if (event == CV_EVENT_LBUTTONUP) {
		std::cout << "x: " << x << "\ty: " << y << std::endl;
	}

	const char *winName = (const char *)param;
	if (event == CV_EVENT_RBUTTONUP) {
		if (!s_blindWindowOpen) {
			setBlindWindow(winName, x, y);

			s_blindWindowOpen = true;
		} else {
			cvShowImage(winName, s_originImage);

			s_blindWindowOpen = false;
		}
	}

	if (s_blindWindowOpen && event == CV_EVENT_MOUSEMOVE) {
		setBlindWindow(winName, x, y);
	}
}

void Debug::trackbarCallback(int pos) {
	s_trackbarPos = pos;
}
